<!--
    Copyright 2015 Alexey Andreev.

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

         http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
-->
<!DOCTYPE html>
<html>
  <head>
    <title>Continuation-passing style demo</title>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
    <script type="text/javascript" charset="utf-8" src="teavm/stdout.js"></script>
    <script type="text/javascript" charset="utf-8" src="js/async.js"></script>
    <script type="text/javascript" charset="utf-8" src="highlight.pack.js"></script>
    <link rel="stylesheet" type="text/css" href="style.css">
    <link rel="stylesheet" type="text/css" href="syntax.css">
    <script type="text/javascript">
      function runAll() {
        hljs.highlightBlock(document.getElementById("source-code"));
        main(["foo", "bar"]);
      }
    </script>
  </head>
  <body onload="runAll()">
    <div id="description">This application shows how TeaVM can handle multiple threads and synchronization primitives
    (see <a href="https://github.com/konsoletyper/teavm/tree/master/samples/async">source code on GitHub</a>).</div>

    <div id="blocks">
      <div class="block" id="stdout-wrapper">
        <div class="block-title">stdout</div>
        <div class="block-content" id="stdout"></div>
      </div>

      <div class="block" id="source-wrapper">
        <div class="block-title">Source code</div>
        <pre class="block-content" id="source-code">
public final class AsyncProgram {
    private static long start = System.currentTimeMillis();

    private AsyncProgram() {
    }

    public static void main(String[] args) throws InterruptedException {
        report(Arrays.toString(args));
        findPrimes();
        withoutAsync();
        report("");
        withAsync();

        report("");
        final Object lock = new Object();
        new Thread(() -> {
            try {
                doRun(lock);
            } catch (InterruptedException ex) {
                report("Exception caught: " + ex.getMessage());
            }
        }, "Test Thread").start();

        new Thread(() -> {
            try {
                doRun(lock);
            } catch (InterruptedException ex) {
                report("Exception caught: " + ex.getMessage());
            }
        }, "Test Thread 2").start();

        report("Should be main");
        report("Now trying wait...");

        synchronized (lock) {
            report("Lock acquired");
            lock.wait(20000);
        }
        report("Finished main thread");
    }

    private static void findPrimes() {
        report("Finding primes");
        boolean[] prime = new boolean[1000];
        prime[2] = true;
        prime[3] = true;
        nextPrime: for (int i = 5; i < prime.length; i += 2) {
            int maxPrime = (int) Math.sqrt(i);
            for (int j = 3; j <= maxPrime; j += 2) {
                Thread.yield();
                if (prime[j] && i % j == 0) {
                    continue nextPrime;
                }
            }
            prime[i] = true;
        }
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 1000; ++i) {
            if (prime[i]) {
                sb.append(i).append(' ');
            }
        }
        report(sb.toString());
    }

    private static void report(String message) {
        long current = System.currentTimeMillis() - start;
        System.out.println("[" + Thread.currentThread().getName() + "]/" + current + ": " + message);
    }

    private static void doRun(Object lock) throws InterruptedException {
        report("Executing timer task");
        Thread.sleep(2000);
        report("Calling lock.notify()");
        synchronized (lock) {
            lock.notify();
        }
        report("Finished calling lock.notify()");
        report("Waiting 5 seconds");
        Thread.sleep(5000);
        report("Finished another 5 second sleep");

        synchronized (lock) {
            report("Sleep inside locked section");
            Thread.sleep(2000);
            report("Finished locked section");
        }
    }

    private static void withoutAsync() {
        report("Start sync");
        for (int i = 0; i < 20; ++i) {
            StringBuilder sb = new StringBuilder();
            for (int j = 0; j <= i; ++j) {
                sb.append(j);
                sb.append(' ');
            }
            report(sb.toString());
        }
        report("Complete sync");
    }

    private static void withAsync() throws InterruptedException {
        report("Start async");
        for (int i = 0; i < 20; ++i) {
            StringBuilder sb = new StringBuilder();
            for (int j = 0; j <= i; ++j) {
                sb.append(j);
                sb.append(' ');
            }
            report(sb.toString());
            if (i % 3 == 0) {
                report("Suspend for a second");
                Thread.sleep(1000);
            }
        }
        report("2nd Thread.sleep in same method");
        Thread.sleep(1000);

        report("Throwing exception");
        try {
            throwException();
        } catch (IllegalStateException e) {
            report("Exception caught");
        }
        report("Complete async");
    }

    private static void throwException() {
        Thread.yield();
        report("Thread.yield called");
        throw new IllegalStateException();
    }
}

</pre>

      </div>
    </div>

  </body>
</html>